// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package ina226

import (
	"encoding/binary"
	"errors"
	"fmt"
	"sync"
	"math"
	"openzbox.com/driver/conn/i2c"
	"openzbox.com/driver/conn/mmr"
	"openzbox.com/driver/conn/physic"
)

// Opts holds the configuration options.
//
// Slave Address
//
// Depending which pins the A1, A0 pins are connected to will change the slave
// address. Default configuration is address 0x40 (both pins to GND). For a full
// address table see datasheet.


// Registers address map
const (
	CONFIG_REG         byte = 0x00
	SHUNTVOLTAGE_REG   byte = 0x01
	BUSVOLTAGE_REG     byte = 0x02
	POWER_REG          byte = 0x03
	CURRENT_REG        byte = 0x04
	CALIBRATION_REG    byte = 0x05
	MASKENABLE_REG     byte = 0x06
	ALERTLIMIT_REG     byte = 0x07
	MANUFACTURERID_REG byte = 0xFE
	DIEID_REG          byte = 0xFF
)

// Configuration register helper constants
//
//   15   14  13  12    11     10     9       8       7       6      5      4      3      2     1     0
//  _____ ___ ___ ___ ______ ______ ______ _______ _______ _______ ______ ______ ______ _____ _____ _____
// |     |   |   |   |      |      |      |       |       |       |      |      |      |     |     |     |
// | RST | - | - | - | AVG2 | AVG1 | AVG0 |VBUSCT2|VBUSCT1|VBUSCT0|VSHCT2|VSHCT1|VSHCT0|MODE3|MODE2|MODE1|
// |_____|__ |___|___|______|______|______|_______|_______|_______|______|______|______|_____|_____|_____|

const (
	// MODE: operating mode (3-bit)
	INA226_MODE_POWER_DOWN     uint16 = 0x00
	INA226_MODE_SHUNT_TRIG     uint16 = 0x01
	INA226_MODE_BUS_TRIG       uint16 = 0x02
	INA226_MODE_SHUNT_BUS_TRIG uint16 = 0x03
	INA226_MODE_ADC_OFF        uint16 = 0x04
	INA226_MODE_SHUNT_CONT     uint16 = 0x05
	INA226_MODE_BUS_CONT       uint16 = 0x06
	INA226_MODE_SHUNT_BUS_CONT uint16 = 0x07

	// VSHCT: shunt voltage conversion time (3-bit)
	INA226_SHUNT_CONV_TIME_140US  uint16 = 0x00 << 3
	INA226_SHUNT_CONV_TIME_204US  uint16 = 0x01 << 3
	INA226_SHUNT_CONV_TIME_332US  uint16 = 0x02 << 3
	INA226_SHUNT_CONV_TIME_588US  uint16 = 0x03 << 3
	INA226_SHUNT_CONV_TIME_1100US uint16 = 0x04 << 3
	INA226_SHUNT_CONV_TIME_2116US uint16 = 0x05 << 3
	INA226_SHUNT_CONV_TIME_4156US uint16 = 0x06 << 3
	INA226_SHUNT_CONV_TIME_8244US uint16 = 0x07 << 3

	// VBUSCT: bus voltage conversion time (3-bit)
	INA226_BUS_CONV_TIME_140US  uint16 = 0x00 << 6
	INA226_BUS_CONV_TIME_204US  uint16 = 0x01 << 6
	INA226_BUS_CONV_TIME_332US  uint16 = 0x02 << 6
	INA226_BUS_CONV_TIME_588US  uint16 = 0x03 << 6
	INA226_BUS_CONV_TIME_1100US uint16 = 0x04 << 6
	INA226_BUS_CONV_TIME_2116US uint16 = 0x05 << 6
	INA226_BUS_CONV_TIME_4156US uint16 = 0x06 << 6
	INA226_BUS_CONV_TIME_8244US uint16 = 0x07 << 6

	// AVG: averaging mode (3-bit)
	INA226_AVERAGES_1    uint16 = 0x00 << 9
	INA226_AVERAGES_4    uint16 = 0x01 << 9
	INA226_AVERAGES_16   uint16 = 0x02 << 9
	INA226_AVERAGES_64   uint16 = 0x03 << 9
	INA226_AVERAGES_128  uint16 = 0x04 << 9
	INA226_AVERAGES_256  uint16 = 0x05 << 9
	INA226_AVERAGES_512  uint16 = 0x06 << 9
	INA226_AVERAGES_1024 uint16 = 0x07 << 9

	// RST bit
	INA226_RST uint16 = 0x01 << 15
)


type Opts struct {
	Address       int
	SenseResistor physic.ElectricResistance
	MaxCurrent    physic.ElectricCurrent
}

// DefaultOpts is the recommended default options.
var DefaultOpts = Opts{
	Address:       0x40,
	SenseResistor: 100 * physic.MilliOhm,
	MaxCurrent:    3200 * physic.MilliAmpere,
}

// New opens a handle to an ina219 sensor.
func New(bus i2c.Bus, opts *Opts) (*Dev, error) {

	i2cAddress := DefaultOpts.Address
	if opts.Address != 0 {
		if opts.Address < 0x40 || opts.Address > 0x4f {
			return nil, errAddressOutOfRange
		}
		i2cAddress = opts.Address
	}
/*
	senseResistor := DefaultOpts.SenseResistor
	if opts.SenseResistor != 0 {
		if opts.SenseResistor < 1 {
			return nil, errSenseResistorValueInvalid
		}
		senseResistor = opts.SenseResistor
	}

	maxCurrent := DefaultOpts.MaxCurrent
	if opts.MaxCurrent != 0 {
		if opts.MaxCurrent < 1 {
			return nil, errMaxCurrentInvalid
		}
		maxCurrent = opts.MaxCurrent
	}
*/
	dev := &Dev{
		m: mmr.Dev8{
			Conn:  &i2c.Dev{Bus: bus, Addr: uint16(i2cAddress)},
			Order: binary.BigEndian,
		},
	}

/*	if err := dev.calibrate(senseResistor, maxCurrent); err != nil {
		return nil, err
	}

	if err := dev.m.WriteUint16(CONFIG_REG, 0x1FFF); err != nil {
		return nil, errWritingToConfigRegister
	}
*/
	return dev, nil
}
func wordToByteArray(w uint16) []byte {
	buf := make([]byte, 2)
	buf[0] = byte(w >> 8)
	buf[1] = byte(w)
	return buf
}
func (ina226 *Dev) Configure(confs ...uint16) error {
	var configuration uint16

	for _, conf := range confs {
		configuration |= conf
	}
	var buf []byte
	//buf = append(buf, CONFIG_REG)
	buf = append(buf, wordToByteArray(configuration)...)

	err := ina226.m.WriteStruct(CONFIG_REG,buf[:])
	if err != nil {
		return err
	}
	return nil
}

// Dev is a handle to the ina219 sensor.
type Dev struct {
	m mmr.Dev8
	mu         sync.Mutex
/*
	currentLSB physic.ElectricCurrent
	powerLSB   physic.Power*/

	// calibration variables
	rShunt, iMax, vBusMax, vShuntMax float64
}
func (ina226 *Dev) ReadBusVoltage() (float64, error) {
	voltage, err := ina226.m.ReadUint16(BUSVOLTAGE_REG)
	if err != nil {
		return 0, err
	}
	return float64(voltage) * 1.25, nil
}

func (ina226 *Dev) ReadShuntVoltage() (float64, error) {
	voltage, err := ina226.m.ReadUint16(SHUNTVOLTAGE_REG)
	if err != nil {
		return 0, err
	}
	return float64(int16(voltage)) * 0.0025*1000, nil
}
var  POWER_LSB   =   2.0
func (ina226 *Dev) ReadPower() (float64, error) {
	voltage, err := ina226.m.ReadUint16(POWER_REG)
	if err != nil {
		return 0, err
	}
	return float64(int16(voltage)) * POWER_LSB , nil
}

// Sense reads the power values from the ina219 sensor.
func (d *Dev) Sense() (PowerMonitor, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	var pm PowerMonitor
	//电阻分压
	shunt, err := d.ReadShuntVoltage()
	if err != nil {
		return PowerMonitor{}, errReadShunt
	}
	// Least significant bit is 10μV.
	pm.Shunt = physic.ElectricPotential(shunt) * physic.MicroVolt
	//总线电压
	vBus, err := d.ReadBusVoltage()
	if err != nil {
		return PowerMonitor{}, errReadBus
	}
	// Least significant bit is 4mV.
	pm.Voltage =physic.ElectricPotential(vBus)* physic.MilliVolt;// physic.ElectricPotential(bus>>3) * 4 * physic.MilliVolt

	/*
	current, err := d.m.ReadUint16(CURRENT_REG)
	if err != nil {
		return PowerMonitor{}, errReadCurrent
	}
	pm.Current = physic.ElectricCurrent(current) //* d.currentLSB
*/

	current, err := d.ReadShuntCurrent()
	if err != nil {
		return PowerMonitor{}, errReadCurrent
	}
	pm.Current = physic.ElectricCurrent(current)* physic.MilliAmpere

	power, err := d.ReadPower()
	if err != nil {
		return PowerMonitor{}, errReadPower
	}
	pm.Power = physic.Power(power)* physic.MilliWatt //*

	return pm, nil
}

func (ina226 *Dev) readCalibrationRegister() (uint16, error) {
	calReg, err := ina226.m.ReadUint16(CALIBRATION_REG)
	if err != nil {
		return 0, err
	}
	return calReg, nil
}
func (ina226 *Dev) CurrentResolution() (float64, error) {
	if ina226.rShunt <= 0.0 {
		return 0, errors.New(fmt.Sprintf("rShunt value: %f is not correct. Must be greater than 0", ina226.rShunt))
	}
	calibration, err := ina226.readCalibrationRegister()
	if err != nil {
		return 0, err
	}
	return 0.00512 / (float64(calibration) * ina226.rShunt), nil
}
func (ina226 *Dev) ReadShuntCurrentRegister() (int16, error) {
	currentRaw, err := ina226.m.ReadUint16(CURRENT_REG)
	if err != nil {
		return 0, err
	}
	return int16(currentRaw), nil
}

func (ina226 *Dev) ReadShuntCurrent() (float64, error) {
	currentRaw, err := ina226.ReadShuntCurrentRegister()
	if err != nil {
		return 0, err
	}

	currentResolution, err := ina226.CurrentResolution()
	if err != nil {
		return 0, err
	}

	return float64(currentRaw) * currentResolution *1000, nil
}
// Since physic electrical is in nano units we need to scale taking care to not
// overflow int64 or loose resolution.
/*
const calibratescale int64 = ((int64(physic.Ampere) * int64(physic.Ohm)) / 100000) << 12
*/
func (ina226 *Dev) Calibrate(rShuntValue float64, iMaxValue float64) error {
	ina226.rShunt = rShuntValue
	ina226.iMax = iMaxValue

	currentLSB := ina226.iMax / 32768
	currentLSB *= 1000000 // transform to micro Ampere
	// As described in the datasheet to simplify calculation we should approximate the current LSB number
	// the method used is following described:
	// first extract from the currentLSB normalized notation only the mantissa
	currentLSB_mantissa := currentLSB / (math.Pow(10, math.Floor(math.Log10(currentLSB))))
	// then apply the ceiling function and multiply for the exponent
	currentLSB_approx := math.Ceil(currentLSB_mantissa) * math.Pow(10, math.Floor((math.Log10(currentLSB))))
	currentLSB = currentLSB_approx / 1000000 //transform back to Ampere

	calibrationValue := uint16((0.00512) / (currentLSB * ina226.rShunt))

	var buf []byte
	//buf = append(buf, CALIBRATION_REG)
	buf = append(buf, wordToByteArray(calibrationValue)...)

	err := ina226.m.WriteStruct(CALIBRATION_REG,buf[:])
	if err != nil {
		return err
	}
	return nil

}

// PowerMonitor represents measurements from ina219 sensor.
type PowerMonitor struct {
	Shunt   physic.ElectricPotential
	Voltage physic.ElectricPotential
	Current physic.ElectricCurrent
	Power   physic.Power
}

// String returns a PowerMonitor as string
func (p PowerMonitor) String() string {
	return fmt.Sprintf("Bus: %s, Current: %s, Power: %s, Shunt: %s", p.Voltage, p.Current, p.Power, p.Shunt)
}

var (
	errReadShunt                 = errors.New("failed to read shunt voltage")
	errReadBus                   = errors.New("failed to read bus voltage")
	errReadPower                 = errors.New("failed to read power")
	errReadCurrent               = errors.New("failed to read current")
	errAddressOutOfRange         = errors.New("i2c address out of range")
	errSenseResistorValueInvalid = errors.New("sense resistor value cannot be negative or zero")
	errMaxCurrentInvalid         = errors.New("max current cannot be negative or zero")
	errRegisterOverflow          = errors.New("bus voltage register overflow")
	errWritingToConfigRegister   = errors.New("failed to write to configuration register")
	errCalibrationOverflow       = errors.New("calibration would exceed maximum scaling")
)
